package com.lcm.openapi.scanner;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.houbb.data.factory.core.util.DataUtil;
import com.lcm.openapi.core.GenerateUtil;
import com.lcm.openapi.config.GenerateConfig;
import com.lcm.openapi.core.api.ApiMethod;
import com.lcm.openapi.core.api.*;
import com.lcm.openapi.generator.NameGenerator;
import org.springframework.http.ResponseEntity;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

public class ApiMethodScanner {

    private final GenerateConfig generateConfig;

    public ApiMethodScanner(GenerateConfig generateConfig) {
        this.generateConfig = generateConfig;
    }

    /**
     * 扫描json，解析出api对象
     *
     * @param dataJson 数据源json
     * @return mode对象
     */
    public List<ApiMethod> scan(JSONObject dataJson) {
        //参考openapi3.0规范
        JSONObject paths = dataJson.getJSONObject("paths");
        if (CollectionUtil.isEmpty(paths)) {
            return new ArrayList<>();
        }
        //遍历,k为请求url，v为请求方法描述, m为请求方法，n为方法描述
        List<ApiMethod> list = new ArrayList<>();
        paths.forEach((k, v) -> {
            //解析接口信息
            this.analyseApiMethod(k, (JSONObject) v, list);
        });
        return list;
    }

    //解析接口信息
    private void analyseApiMethod(String url, JSONObject urlJson, List<ApiMethod> list) {
        if (CollectionUtil.isNotEmpty(urlJson)) {
            //名称生成器
            NameGenerator nameGenerator = generateConfig.getNameGenerator();
            urlJson.forEach((k, v) -> {
                //解析接口信息
                ApiMethod apiMethod = this.analyseApiMethod(url, k, (JSONObject) v);
                //接口方法名
                apiMethod.setMethodName(nameGenerator.generateApiMethodName(apiMethod));
                //去掉空引用
                Set<String> imports = apiMethod.getImports();
                imports.remove(null);
                //返回值枚举
                if (apiMethod.isResponseValue()) {
                    String response = apiMethod.getResponse();
                    String responseEntity = apiMethod.getResponseEntity();
                    if (StrUtil.isNotEmpty(responseEntity)) {
                        response = response + "<" + responseEntity + ">";
                    } else {
                        response = response + "<Void>";
                    }
                    apiMethod.setResponse(response);
                }else{
                    apiMethod.setResponse("void");
                }
                list.add(apiMethod);
            });
        }
    }

    //解析接口信息
    private ApiMethod analyseApiMethod(String url, String requestMethod, JSONObject methodJson) {
        ApiMethod apiMethod = new ApiMethod();
        //所属标签
        apiMethod.setTags(String.valueOf(methodJson.getJSONArray("tags").get(0)));
        //请求方法
        apiMethod.setHttpMethod(StrUtil.upperFirst(requestMethod));
        //请求url
        apiMethod.setUrl(url);
        //接口描述
        apiMethod.setSummary(methodJson.getString("summary"));
        //详细描述
        apiMethod.setDescription(methodJson.getString("description"));
        //解析请求参数
        this.analyseParameter(methodJson, apiMethod);
        //解析requestBody
        this.analyseRequestBody(methodJson, apiMethod);
        //解析responseBody
        this.analyseResponseBody(methodJson, apiMethod);
        //处理分隔符
        this.analyseSplit(apiMethod);
        //完成解析
        return apiMethod;
    }

    //解析parameter
    private void analyseParameter(JSONObject methodJson, ApiMethod apiMethod) {
        JSONArray parameters = methodJson.getJSONArray("parameters");
        if (CollectionUtil.isNotEmpty(parameters)) {
            //path参数
            List<ApiMethodPathVar> pathVars = new ArrayList<>();
            //query参数
            List<ApiMethodUrlVar> urlVars = new ArrayList<>();

            for (int i = 0; i < parameters.size(); i++) {
                JSONObject parameterJson = parameters.getJSONObject(i);
                String in = parameterJson.getString("in");
                String fieldName = parameterJson.getString("name");
                String fieldDesc = parameterJson.getString("description");
                boolean required = parameterJson.getBoolean("required");
                String type = parameterJson.getJSONObject("schema").getString("type");
                //引入相关包
                apiMethod.getImports().add(GenerateUtil.findDependency(type));
                switch (in) {
                    case "path":
                        ApiMethodPathVar pathVar = new ApiMethodPathVar();
                        pathVar.setFieldName(fieldName);
                        pathVar.setFieldDesc(fieldDesc);
                        pathVar.setRequired(required);
                        pathVar.setFieldType(GenerateUtil.toJavaType(type));
                        //如果javaType是list,则需要解析泛型
                        pathVar.setFieldType(GenerateUtil.transJavaListGeneric(pathVar.getFieldType(), parameterJson, apiMethod.getRefRequestModel()));
                        pathVar.setSplit(",\n");
                        pathVars.add(pathVar);
                        break;
                    case "query":
                        ApiMethodUrlVar urlVar = new ApiMethodUrlVar();
                        urlVar.setFieldName(fieldName);
                        urlVar.setFieldDesc(fieldDesc);
                        urlVar.setRequired(required);
                        urlVar.setFieldType(GenerateUtil.toJavaType(type));
                        //如果javaType是list,则需要解析泛型
                        urlVar.setFieldType(GenerateUtil.transJavaListGeneric(urlVar.getFieldType(), parameterJson, apiMethod.getRefRequestModel()));
                        urlVar.setSplit(",\n");
                        urlVars.add(urlVar);
                        break;
                }
            }
            apiMethod.setPathVars(pathVars);
            apiMethod.setUrlVars(urlVars);
        }
    }

    //解析body
    private void analyseRequestBody(JSONObject methodJson, ApiMethod apiMethod) {
        //解析body
        JSONObject requestBody = methodJson.getJSONObject("requestBody");
        if (Objects.nonNull(requestBody)) {
            JSONObject content = requestBody.getJSONObject("content");
            if (Objects.nonNull(content)) {
                content.forEach((k, v) -> {
                    //解析body
                    this.analyseRequestBody(apiMethod, k, (JSONObject) v);
                });
            }
        }
    }

    private void analyseResponseBody(JSONObject methodJson, ApiMethod apiMethod) {
        //解析返回结果
        JSONObject responses = methodJson.getJSONObject("responses");
        if (CollectionUtil.isNotEmpty(responses)) {
            responses.forEach((k, v) -> {
                //解析responseBody
                this.analyseResponseBody(apiMethod, k, (JSONObject) v);
            });
        }
    }

    private void analyseResponseBody(ApiMethod apiMethod, String httpStatusCode, JSONObject resBodyJson) {
        //只处理成功的请求
        if (!generateConfig.getHttpSuccessCode().equals(httpStatusCode)) {
            return;
        }
        JSONObject content = resBodyJson.getJSONObject("content");
        if (CollectionUtil.isNotEmpty(content)) {
            content.forEach((k, v) -> {
                //解析responseBody
                this.analyseJsonResponseBody(apiMethod, k, (JSONObject) v);
            });
        }
    }

    private void analyseJsonResponseBody(ApiMethod apiMethod, String contentType, JSONObject contentJson) {
        JSONObject schema = contentJson.getJSONObject("schema");
        if (ContentType.JSON.getValue().equals(contentType)) {
            //统一请求返回类型
            apiMethod.setResponse(StrUtil.isNotEmpty(generateConfig.getCommonResponseClass()) ? generateConfig.getCommonResponseClass() : ResponseEntity.class.getSimpleName());
            apiMethod.setResponseValue(true);
            //只处理JSON的返回结果
            JSONObject properties = schema.getJSONObject("properties");
            if (CollectionUtil.isNotEmpty(properties)) {
                //判断是否存在公共数据字段
                JSONObject commonDataJson = properties.getJSONObject(generateConfig.getCommonDataKey());
                if (CollectionUtil.isNotEmpty(commonDataJson)) {
                    String ref = null;
                    //判断内容是否为普通泛型对象
                    if (commonDataJson.containsKey(GenerateUtil.JSON_CIRCLE_KEY)) {
                        ref = GenerateUtil.analyseRef(commonDataJson);
                        apiMethod.setResponseEntity(ref);
                    } else if (commonDataJson.containsKey("items")) {
                        //判断内容是否为数组
                        JSONObject itemJson = commonDataJson.getJSONObject("items");
                        ref = GenerateUtil.analyseRef(itemJson);
                        apiMethod.setResponseEntity(ref);
                    } else if (commonDataJson.containsKey("properties")) {
                        //判断是否为分页对象
                        JSONObject recordJson = commonDataJson.getJSONObject("properties").getJSONObject(generateConfig.getCommonPageKey());
                        if (Objects.nonNull(recordJson) && recordJson.containsKey("items")) {
                            JSONObject itemJson = recordJson.getJSONObject("items");
                            ref = GenerateUtil.analyseRef(itemJson);
                            String commonPageResponseClass = generateConfig.getCommonPageResponseClass();
                            if (StrUtil.isNotEmpty(commonPageResponseClass)) {
                                apiMethod.setResponseEntity(commonPageResponseClass + "<" + ref + ">");
                                apiMethod.setPageResponseEntity(ref);
                            } else {
                                apiMethod.setResponseEntity(ref);
                            }
                        }
                        apiMethod.setPageResult(true);
                    }
                    if (StrUtil.isNotEmpty(ref)) {
                        apiMethod.getRefResponseModel().add(ref);
                    }
                }
            }
        } else {
            //认为是无返回值
            apiMethod.setResponse("void");
            apiMethod.setResponseValue(false);
        }
    }

    //解析body
    private void analyseRequestBody(ApiMethod apiMethod, String contentType, JSONObject contentJson) {
        JSONObject schema = contentJson.getJSONObject("schema");
        if (CollectionUtil.isNotEmpty(schema)) {
            if (ContentType.JSON.getValue().equals(contentType)) {
                //requestBody是json的情况处理
                this.analyseJsonBody(schema, apiMethod);
            } else if (ContentType.MULTIPART.getValue().equals(contentType)) {
                //requestBody是form-data的情况处理
                this.analyseFormBody(schema, apiMethod);
            }
        }
    }

    //处理requestBody是json的情况
    private void analyseJsonBody(JSONObject schema, ApiMethod apiMethod) {
        //requestBody是json的情况处理
        ApiMethodBodyVar bodyVar = new ApiMethodBodyVar();
        //判断是否存在ref, 存在则作为对象
        if (schema.containsKey(GenerateUtil.JSON_CIRCLE_KEY)) {
            String ref = GenerateUtil.analyseRef(schema);
            if (StrUtil.isNotEmpty(ref)) {
                bodyVar.setClassName(ref);
                bodyVar.setFieldName(StrUtil.lowerFirst(ref));
                apiMethod.getRefRequestModel().add(ref);
            }
        } else {
            bodyVar.setClassName("Map<String,Object>");
            bodyVar.setFieldName("map");
            apiMethod.getImports().add("import java.util.Map;");
        }
        apiMethod.setBodyJsonVar(bodyVar);
    }

    //处理requestBody是form-data的情况
    private void analyseFormBody(JSONObject schema, ApiMethod apiMethod) {
        //requestBody是form-data的情况处理
        List<String> required = new ArrayList<>();
        if (schema.containsKey("required")) {
            required.addAll(schema.getJSONArray("required").toJavaList(String.class));
        }

        JSONObject propJson = schema.getJSONObject("properties");
        if (CollectionUtil.isNotEmpty(propJson)) {
            List<ApiMethodMultiVar> multiVars = new ArrayList<>();
            propJson.forEach((x, y) -> {
                ApiMethodMultiVar multiVar = this.analyseFormBody(apiMethod, x, (JSONObject) y, required);
                multiVars.add(multiVar);
            });
            apiMethod.setMultiVars(multiVars);
        }
    }

    //处理requestBody是form-data的字段
    private ApiMethodMultiVar analyseFormBody(ApiMethod apiMethod, String fieldName, JSONObject prop, List<String> required) {
        String format = prop.getString("format");
        String type = prop.getString("type");
        //引入相关包
        apiMethod.getImports().add(GenerateUtil.findDependency(type));
        String fieldDesc = prop.getString("description");

        ApiMethodMultiVar multiVar = new ApiMethodMultiVar();
        multiVar.setFile(StrUtil.isNotEmpty(format) && "binary".equals(format));
        if (multiVar.isFile()) {
            apiMethod.getImports().add("import org.springframework.web.multipart.MultipartFile;");
        }
        multiVar.setRequired(required.contains(fieldName));
        multiVar.setFieldName(fieldName);
        multiVar.setFieldDesc(fieldDesc);
        multiVar.setFieldType(GenerateUtil.toJavaType(type));
        multiVar.setSplit(",\n");
        //如果javaType是list,则需要解析泛型
        multiVar.setFieldType(GenerateUtil.transJavaListGeneric(multiVar.getFieldType(), prop, apiMethod.getRefRequestModel()));
        return multiVar;
    }

    //处理参数分隔符
    private void analyseSplit(ApiMethod apiMethod) {
        List<ApiMethodUrlVar> urlVars = apiMethod.getUrlVars();
        if (CollectionUtil.isNotEmpty(urlVars)) {
            ApiMethodUrlVar urlVar = urlVars.get(urlVars.size() - 1);
            urlVar.setSplit(null);
            return;
        }
        List<ApiMethodPathVar> pathVars = apiMethod.getPathVars();
        if (CollectionUtil.isNotEmpty(pathVars)) {
            ApiMethodPathVar pathVar = pathVars.get(pathVars.size() - 1);
            pathVar.setSplit(null);
            return;
        }
        List<ApiMethodMultiVar> multiVars = apiMethod.getMultiVars();
        if (CollectionUtil.isNotEmpty(multiVars)) {
            ApiMethodMultiVar multiVar = multiVars.get(multiVars.size() - 1);
            multiVar.setSplit(null);
        }
    }

}
